-- ItemM
-- Create by weism
-- 管理所有的普通道具

module("ItemM", package.seeall);

-- 所有的道具信息
local items = {};

-- 物品类型表
local itemGroupConfig = {};

-- 子模块
local rules = {};

-- 卷轴表
local allScroll = {};
local allEquip  = {};
local allMaterial = {};
local allFoodMaterial = {};
local allDeviceMaterial = {};

-- 可携带道具
local allCarriablePropety = {};

-- 可带出地牢的道具
local allTakeOutItems = {};

-- 载入配置表
local _init = false;
local loadCsv = function()
    if _init then return end
    _init = true;

    allScroll = {};
    allEquip  = {};
    allMaterial = {};
    allAlchemy = {};
    allCarriablePropety = {};
    allFoodMaterial = {};
    allTakeOutItems = {};
    allDeviceMaterial = {};

    -- 转换下item信息表
    local item_CSV = get_item_CSV();
    for i = 1, #item_CSV do
        items[item_CSV[i].class_id] = item_CSV[i];

        -- 出售价格
        items[item_CSV[i].class_id].sell_price = parsePrice(item_CSV[i].sell_price)

        -- 购买价格
        items[item_CSV[i].class_id].buy_price = parsePrice(item_CSV[i].buy_price)
    end

    -- 整理卷轴表(需排除掉特殊魔法)
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_SPELL and item_CSV[i].group ~= 33 then
            -- 是卷轴
            if nil == allScroll[item_CSV[i].rank] then
                allScroll[item_CSV[i].rank] = { item_CSV[i].class_id };
            else
                table.insert(allScroll[item_CSV[i].rank], item_CSV[i].class_id);
            end
        end
    end

    -- 整理宝物表
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_TREASURE and item_CSV[i].group ~= 12 and item_CSV[i].group ~= 13 then
            -- 是卷轴
            if nil == allEquip[item_CSV[i].rank] then
                allEquip[item_CSV[i].rank] = { item_CSV[i].class_id };
            else
                table.insert(allEquip[item_CSV[i].rank], item_CSV[i].class_id);
            end
        end
    end

    -- 整理材料
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_MATERIAL and item_CSV[i].group == 14 then
            -- 是材料
            table.insert(allMaterial, item_CSV[i].class_id);
        end
    end

    -- 整理炼金材料
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_MATERIAL and item_CSV[i].group == 15 then
            -- 是炼金材料
            table.insert(allAlchemy, item_CSV[i].class_id);
        end
    end

    -- 整理食材
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_MATERIAL and item_CSV[i].group == 20 then
            -- 是材料
            table.insert(allFoodMaterial, item_CSV[i].class_id);
        end
    end

    -- 整理飞艇材料
    for i = 1, #item_CSV do
        if item_CSV[i].type == ITEM_TYPE_DEVICE_MATERIAL and item_CSV[i].group == 32 then
            -- 是飞艇材料
            table.insert(allDeviceMaterial, item_CSV[i].class_id);
        end
    end

    -- 收集可携带item
    for i = 1, #item_CSV do
        if item_CSV[i].group == 27 then
            -- 可携带道具
            table.insert(allCarriablePropety, item_CSV[i].class_id);
        end
    end

    item_CSV = {};

    -- 转换物品类型表
    local takeOutGroup = {};     -- 可携带出迷宫
    local item_group_CSV = get_item_group_CSV();
    for i = 1, #item_group_CSV do
        itemGroupConfig[item_group_CSV[i].group] = item_group_CSV[i];

        if item_group_CSV[i].take_out == 1 then
            table.insert(takeOutGroup, item_group_CSV[i].group);
        end
    end

    item_group_CSV = {};

    -- 记录下可携带出迷宫的ID
    for classId, brief in pairs(items) do
        -- 获取物品详细信息
        local group = ItemM.query(classId, "group");
        -- 找出符合要求的物品
        if table.indexOf(takeOutGroup, group) ~= -1 then
            allTakeOutItems[classId] = 1;
        end
    end

    -- 排序一遍，确保和服务器的顺序一致
    for _, rank in pairs(table.keys(allScroll)) do
        table.sort(allScroll[rank]);
    end
    for _, rank in pairs(table.keys(allEquip)) do
        table.sort(allEquip[rank]);
    end
end

-- 模块初始化
function init()
    if not _init then
        loadCsv();

        -- 载入所有的规则处理子模块
        rules = LOAD_PATH("game/logic/module/item");
    end
end

-- 解析商品价格
function parsePrice(price)
    -- 没有价格
    if type(price) ~= 'string' then
        return
    end

    local desc = string.explode(price, "|")
    local ret = {}
    for index, info in pairs(desc) do
        local arr = string.explode(string.sub(info, 3, -2), ",")
        if string.startWith(info, "1(") then
            -- 物品
            ret[index] = { 1, tonumber(arr[1]), tonumber(arr[2]), }
        elseif string.startWith(info, "2(") then
            -- 属性
            ret[index] = { 2, arr[1], tonumber(arr[2]), }
        end
    end

    return ret;
end

-- 检索道具信息
function query(classId, path)
    local m = items[classId];
    if nil == m then
        return nil;
    end
    if (path == "" or path == nil) then
        return m;
    else
        return m[path];
    end
end

-- 通过道具名获得道具ID
function getItemsIdByName(name)
    local itemIds = {};
    local i = 1;
    for key, value in pairs(items) do
        if value.name == name then
            itemIds[i] = value.class_id;
            i = i + 1;
        end
    end
    return itemIds;
end

-- 获取道具描述
function getItemDesc(id, para)
    local desc = query(id, "desc");
    local args = query(id, "desc_args") or {};
    local pre  = "script_";

    if string.startWith(desc, pre) == true then
        -- 配置了描述脚本，调用脚本处理
        local rule = string.sub(desc, string.len(pre) + 1);
        local f = rules[rule];
        return f.getDesc(id, args, para or {});
    else
        return desc;
    end
end

-- 获取道具详细信息
function getItemDetail(id, para, color)
    local detail = query(id, "detail");
    local args   = query(id, "detail_args") or {};
    local pre    = "script_";
    local desc   = "";

    if string.startWith(detail, pre) == true then
        -- 配置了描述脚本，调用脚本处理
        local rule = string.sub(detail, string.len(pre) + 1);
        local f = rules[rule];
        desc = f.getDetail(id, args, para or {});
    else
        desc = detail;
    end

    -- 有需求添加颜色的
    if type(color) == "string" and string.startWith(color, "[") then
        desc = color.. desc .. "[-]";
    end

    -- 工程学手册对道具的额外加成，显示为绿色
    local extraDesc = EngineeringManualM.getItemAdditionDesc(id);
    if extraDesc and extraDesc ~= "" then
        desc = desc .. "\n[green]" .. extraDesc .. "[-]";
    end

    return desc
end

-- 为玩家添加一个普通道具
function updateItem(user, classId, amount)
    local items = user.items;
    if (classId == nil) then
        return;
    end

    local oldCount = getAmount(user, classId);
    if oldCount == amount then
        -- 数量不变，不要乱抛事件了
        return;
    end

    -- 不能超出最大值
    local maxAmount = getMaxAmount(classId);
    amount = iif(amount > maxAmount, maxAmount, amount);

    if amount <= 0 then
        if (items[classId] ~= nil) then
            items[classId] = nil;
        end
    else
        if (items[classId] ~= nil) then
            items[classId].amount = amount;
        else
            items[classId] = Item.new(classId, amount, user.rid);
        end
    end

    -- 抛出道具数量更新的事件
    EventMgr.fire(event.ITEM_COUNT_UPDATED, classId);
end

-- 获取配置表中的所有物品
function getAllItems()
    return items;
end

function getMaxAmount(classId)
    local max = query(classId, "max_amount");

    if type(max) ~= "number" or max <= 0 then
        return 65535;
    end

    return max;
end

-- 物品数量
function getAmount(user, classId)
    local items = user.items

    if not items then
        return 0;
    end

    -- 如果没有此物品
    if not items[classId] then
        return 0
    else
        return items[classId].amount
    end
end

-- 拥有物品数量
function getOwnedAmount(user, classId)
    local amount = getAmount(user, classId);

    if EquipM.isEquiped(classId) then
        return amount + 1;
    else
        return amount;
    end
end

-- 扣除物品
function costAmount(user, classId, amount)
    local items = user.items;

    -- 该物品数量不足
    if not items[classId] or items[classId].amount < amount then
        return false
    end

    updateItem(user, classId, items[classId].amount - amount)

    return true
end

-- 获得物品
function addAmount(user, classId, amount, extra)
    local items = user.items;

    -- 该物品数量不足
    if not items[classId] then
        updateItem(user, classId, amount);
    else
        updateItem(user, classId, items[classId].amount + amount);
    end

    local para = { ["classId"] = classId, ["amount"] = amount, };
    if type(extra) == 'table' then
        if extra.existed == true then
            -- 这是已有的物品（例如从身上脱下的装备），无需抛出获得物品的事件
            return;
        end

        -- 吸入传入的参数
        para = table.add(para, extra);
    end

    -- 抛出玩家获得物品的事件
    EventMgr.fire(event.BONUS_ITEM, para);
end

-- 获取玩家身上的神器
function getArtifact()
    return getItemsByType(ME.user, ITEM_TYPE_ARTIFACT);
end

-- 获取玩家身上的神器碎片
function getArtifactScrap()
    return getItemsByType(ME.user, ITEM_TYPE_ARTIFACT_SCRAP);
end

-----------------------------------------------
-- 根据物品类型筛选物品
-- @function [parent=#ItemM] getItemsByType
-- @param user     玩家对象
-- @param itemType 道具类型，常量定义在item.lua中
function getItemsByType(user, itemType)
    local items = user:getItems();
    local ret = {};
    for classId, brief in pairs(items) do
        -- 获取物品详细信息
        local type = ItemM.query(classId, "type");
        -- 找出符合要求的物品
        if itemType == type and ItemM.getAmount(user, classId) > 0 then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-----------------------------------------------
-- 根据物品组别筛选物品
-- @function [parent=#ItemM] getItemsByGroup
-- @param user     玩家对象
-- @param itemGroup 道具类型，常量定义在itemGroup.lua中
function getItemsByGroup(user, itemGroup)
    local items = user:getItems();
    local ret = {};
    for classId, brief in pairs(items) do
        -- 获取物品详细信息
        local group = ItemM.query(classId, "group");
        -- 找出符合要求的物品
        if itemGroup == group and ItemM.getAmount(user, classId) > 0 then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-- 获取道具数量（去除带入迷宫的个数）
function getAmount2(user, classId)
    local num = getAmount(user, classId);

    local dungeonItems = ME.user.dbase:query("dungeon_item") or {};
    for itemId, n in pairs(dungeonItems) do
        if itemId == classId then
            num = num - n;
            break;
        end
    end

    if num < 0 then
        num = 0;
    end

    return num;
end

-- 根据物品组别筛选物品（去除带入迷宫的个数）
function getItemsByGroup2(user, itemGroup)
    local items = user:getItems();
    local ret = {};
    for classId, brief in pairs(items) do
        -- 获取物品详细信息
        local group = ItemM.query(classId, "group");
        -- 找出符合要求的物品
        if itemGroup == group and ItemM.getAmount2(user, classId) > 0 then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-----------------------------------------------
-- 根据类别筛选卷轴
-- @function [parent=#ItemM] getItemsBySkillRank
-- @param user     玩家对象
-- @param genre    类别
function getItemsBySkillGenre(user, genre)
    local items = getItemsByType(user, ITEM_TYPE_SPELL);
    local ret = {};
    for _, classId in pairs(items) do

        -- 找出技能系别匹配的物品
        if SpellM.query(classId, "genre") == genre then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-----------------------------------------------
-- 根据阶位筛选卷轴
-- @function [parent=#ItemM] getItemsBySkillRank
-- @param user     玩家对象
-- @param rank    阶位
function getItemsBySkillRank(user, rank)
    local items = getItemsByType(user, ITEM_TYPE_SPELL);
    local ret = {};
    for _, classId in pairs(items) do
        -- 获取卷轴对应技能ID
        local skillId = SpellM.query(classId, "cast_skill_id");

        -- 找出技能系别匹配的物品
        if SkillM.query(skillId, "rank") == rank then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-----------------------------------------------
-- 根据技能系别筛选卷轴
-- @function [parent=#ItemM] getItemsBySkillStyle
-- @param user     玩家对象
-- @param style    技能系别，常量定义在logic/base/style.lua中 (FIRE/WATER/THUNDER等)
function getItemsBySkillStyle(user, style)
    local items = getItemsByType(user, ITEM_TYPE_SPELL);
    local ret = {};
    for _, classId in pairs(items) do
        -- 获取卷轴对应技能ID
        local skillId = SpellM.query(classId, "cast_skill_id");

        -- 找出技能系别匹配的物品
        if SkillM.query(skillId, "style") == style then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-----------------------------------------------
-- 获取用户能使用的卷轴
-- @function [parent=#ItemM] getItemsBySkillStyle
-- @param user     玩家对象

function getSpellCanUse()
    local items = getItemsByType(ME.user, ITEM_TYPE_SPELL);
    local ret = {};
    for _, classId in pairs(items) do
        -- 获取卷轴对应技能ID
        local skillId = SpellM.query(classId, "cast_skill_id");

        local canUse = true;
        -- 1.无法使用，需要技能支持
        if true ~= FormulaM.invoke("CAN_USE_SPELL", classId) then
            canUse = false;
        end

        if canUse then
            canUse = false;

            -- 2.魔法值不足
            local curMp = ME.user:getMp() or 0;
            if SpellM.calcSpellCost(classId) <= curMp then
                canUse = true;
            end
        end

        if canUse then
            table.insert(ret, classId);
        end
    end
    return ret;
end

-- 更新物品图鉴信息
function updateItemIllu(user, classId)
    if isItemIlluActivate(user, classId) then
        return;
    end
    local itemIllu = user.itemIllu;
    table.insert(itemIllu, classId);
end

-- 判断某物品图鉴是否激活
function isItemIlluActivate(user, classId)
    return table.indexOf(user.itemIllu, classId) >= 1;
end

-- 获取对应阶位的卷轴
function getScrollByRank(rank, simple)
    local list = allScroll[rank];
    if not simple then
        return list;
    end

    local ret = {};
    for _, classId in pairs(list) do
        if query(classId, "simple") == 1 then
            table.insert(ret, classId);
        end
    end

    return ret;
end

-- 获取对应阶位的宝物
function getEquipByRank(rank, simple, itemType)
    local tempList = allEquip[rank];
    local itemType = itemType or ITEM_TYPE_TREASURE;
    local ret = {};
    for _, classId in pairs(tempList) do
        if query(classId, "type") == itemType then
            if not simple or query(classId, "simple") == 1 then
                table.insert(ret, classId);
            end
        end
    end
    return ret;
end

-- 获取所有的合成材料
function getAllMaterial()
    return allMaterial;
end

-- 获取所有的炼金材料
function getAllAlchemy()
    return allAlchemy;
end

function updateNewItemList(classId)
    local newItemList = ME.user.dbase:queryTemp("new_item_list", {});
    table.removeItem(newItemList, classId);
    table.insert(newItemList, classId);
    ME.user.dbase:setTemp("new_item_list", newItemList);
end

function cleanNewItemList(classId)
    if classId == nil then
        -- 全部清除
        ME.user.dbase:setTemp("new_item_list", {});
    else
        local newItemList = ME.user.dbase:queryTemp("new_item_list", {});
        table.removeItem(newItemList, classId);
        ME.user.dbase:setTemp("new_item_list", newItemList);
    end
end

function isNewItem(classId)
    local newItemList = ME.user.dbase:queryTemp("new_item_list", {});

    if table.indexOf(newItemList, classId) ~= -1 then
        return true;
    end

    return false
end

-- 是否可携带的道具
function isCarriableItem(classId)
    return query(classId, "group") == 27;
end

-- 获得所有可携带的道具
function getAllCarriableItems()
    return allCarriablePropety;
end

-- 是否可被带出道具
function isTakeOutItem(classId)
    return allTakeOutItems[classId] == 1;
end

-- 地牢中可以被带出的道具
function getAllTakeOutItems()
    return allTakeOutItems;
end

-- 客户端携带道具
-- @param classId 道具ID
-- @param amount 道具数量
function carryDungeonItem(classId, amount)
     if true ~= canCarryDungeonItem(classId, amount) then
         return false;
     end

    -- 记录玩家带进地牢的 携带的可以带进迷宫的道具
    local carriableItems = getAllCarriableItems();
    local data = {};
    for k, value in pairs(carriableItems)do
        if classId == value then
            data[value] = amount;
        else
            data[value] = 0;
        end
    end
    ME.user.dbase:set("dungeon_item", data);
    return true;
end

-- 是否可以携带
function canCarryDungeonItem(classId, amount)
    local  itemAmount = getAmount(ME.user, classId);
    if not itemAmount then
        return false
    end

    if itemAmount < amount then
        return false;
    end

    local itemRank = query(classId, "rank");
    if not itemRank then
        return false
    end

    if amount > FormulaM.invoke("CALC_DUNGEON_ITEM_MAX_AMOUNT", itemRank) then
        return false;
    end

    return true;
end

-- 判断是否拥有指定道具或者可以合成该道具
function haveItemOrCanCompound(user, classId)
    if ItemM.getAmount(user, classId) > 0 then
        return true;
    end

    local ruleId = CompoundM.getCompoundRule(classId);
    if true == CompoundM.canDoCompound(ruleId) then
        return true;
    end

    return nil;
end

-- 获取食材
function getFoodMaterial(rank)
    if rank == nil then
        -- 返回全部食材
        return allFoodMaterial;
    else
        local arr = {};
        for _, id in ipairs(allFoodMaterial) do
            if query(id, "rank") == rank then
                table.insert(arr, id);
            end
        end
        return arr;
    end
end

-- 出售价格
function sellItem(classId, sellPrice, count)
    if count == nil then
        count = 1;
    end

    if count > 65535 then
        count = 65535;
    end

    -- 物品是否足够
    local amount = ItemM.getAmount(ME.user, classId);
    if amount < count then
        return false;
    end

    -- 扣除物品
    if not ItemM.costAmount(ME.user, classId, count) then
        cclog("cost amount fail");
        return false;
    end

    -- 根据当前价格和数量执行奖励
    for k, price in pairs(sellPrice) do
        price[3] = price[3] * count;
        BonusM.doBonus(price, "sell_to_money");
    end

    return true;
end

-- 是否是食材
function isFoodMaterial(classId)
    return query(classId, "group") == 20;
end

-- 按阶位获取飞艇盖章材料
function getDeviceMaterialByRank(rank)
    if rank == nil then
        -- 返回全部
        return allDeviceMaterial;
    else
        local arr = {};
        for _, id in ipairs(allDeviceMaterial) do
            if query(id, "rank") == rank then
                table.insert(arr, id);
            end
        end
        return arr;
    end
end
